


    /***
     *
     * g：generate是将AST转化成render funtion字符串的过程，
     * g：得到结果是render的字符串以及staticRenderFns字符串
     *
     */
    function generate(ast,options) {

        var options = {
            expectHTML: true,
            modules: modules$1,
            directives: directives$1,
            isPreTag: isPreTag,
            isUnaryTag: isUnaryTag,
            mustUseProp: mustUseProp,
            canBeLeftOpenTag: canBeLeftOpenTag,
            isReservedTag: isReservedTag,
            getTagNamespace: getTagNamespace,
            staticKeys: genStaticKeys(modules$1)
        };

        var state = new CodegenState(options);
        var code = ast ? genElement(ast, state) : '_c("div")';

        return {

            // g：with 是限定作用域，在里面直接访问一个变量，就会从指定的对象中查找，而不是 window 中
            // g：render 在 Vue.prototype._render 中会绑定 Proxy包围过的 Vm
            // g：括号没有用，去掉也一样
            // g：如果我直接在 with 里面想使用 console.log，但是一样会先从 this 中去获取 console
            // g：然后 proxy 发现 this 中 console 找不到，就触发了 proxy 的 has 方法
            // g：从而被 vue 监听到，说我这个方法不存在
            render: ("with(this){  return " + code + "}"),

            // g：调用 genElement 时，发现是 静态节点，会直接 push进 staticRenderFns 中
            staticRenderFns: state.staticRenderFns

        }

    }

    // hoist static sub-trees out
    function genStatic(el, state) {

        el.staticProcessed = true;
        state.staticRenderFns.push(("with(this){return " + (genElement(el, state)) + "}"));

        return ("_m(" + (state.staticRenderFns.length - 1) + (el.staticInFor ? ',true' : '') + ")")
    }

    // v-once
    function genOnce(el, state) {

        el.onceProcessed = true;

        if (el.if && !el.ifProcessed) {
            return genIf(el, state)

        } else if (el.staticInFor) {

            var key = '';
            var parent = el.parent;

            while (parent) {

                if (parent.for) {
                    key = parent.key;
                    break
                }

                parent = parent.parent;
            }
 

            if (!key) {
                "development" !== 'production' && state.warn(
                    "v-once can only be used inside v-for that is keyed. "
                );
                return genElement(el, state)
            }

            return ("_o(" + (genElement(el, state)) + "," + (state.onceId++) + "," + key + ")")

        } else {

            return genStatic(el, state)
        }
    }

    function genIf(
        el, state,  altGen, altEmpty
    ) {
        el.ifProcessed = true; // avoid recursion

        var _get =  genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)
        // console.log('genIf return --->',_get,);
        return _get
    }

    function genIfConditions(
        conditions,  state,  altGen,  altEmpty
    ) {

        // g：当 没有条件的时候，就 返回 _e
        if (!conditions.length) {
            return altEmpty || '_e()'
        }

        // g：遍历一遍之后，就把条件剔除
        var condition = conditions.shift();

        // console.log('genIfConditions--->',condition.exp,("(" + (condition.exp) + ")?" + 
        // (genTernaryExp(condition.block)) + ":" + 
        // (genIfConditions(conditions, state, altGen, altEmpty))),);

        if (condition.exp) {

            return ("(" + (condition.exp) + ")?" + 
                (genTernaryExp(condition.block)) + ":" + 
                (genIfConditions(conditions, state, altGen, altEmpty)))

        } else {
            return ("" + (genTernaryExp(condition.block)))
        }

        // v-if with v-once should generate code like (a)?_m(0):_m(1)
        function genTernaryExp(el) {
            return altGen ?
                altGen(el, state) :
                el.once ? genOnce(el, state) :genElement(el, state)
        }
    } 

    function genFor(
        el,  state, altGen, altHelper
    ) {

        var exp = el.for;
        var alias = el.alias;
        var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : '';
        var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : '';

        if ("development" !== 'production' &&
            state.maybeComponent(el) &&
            el.tag !== 'slot' &&
            el.tag !== 'template' &&
            !el.key
        ) {
            state.warn(
                "<" + (el.tag) + " v-for=\"" + alias + " in " + exp + "\">: component lists rendered with " +
                "v-for should have explicit keys. " +
                "See https://vuejs.org/guide/list.html#key for more info.",
                true /* tip */
            );
        }

        el.forProcessed = true; // avoid recursion

        return (altHelper || '_l') + "((" + exp + ")," +
            "function(" + alias + iterator1 + iterator2 + "){" +
            "return " + ((altGen || genElement)(el, state)) +
            '})'
    }

    function genData$2(el, state) {

        
        // log_event("genData$2 el",el)

        var data = '{';

        // directives first.
        // directives may mutate the el's other properties before they are generated.
        var dirs = genDirectives(el, state);
        if (dirs) { data += dirs + ','; }

        // key
        if (el.key) {
            data += "key:" + (el.key) + ",";
        }

        // ref
        if (el.ref) {
            data += "ref:" + (el.ref) + ",";
        }
        if (el.refInFor) {
            data += "refInFor:true,";
        }

        // pre
        if (el.pre) {
            data += "pre:true,";
        }

        // record original tag name for components using "is" attribute
        if (el.component) {
            data += "tag:\"" + (el.tag) + "\",";
        }

        // module data generation functions
        for (var i = 0; i < state.dataGenFns.length; i++) {
            data += state.dataGenFns[i](el);
        }

        // attributes
        // g：全部属性
        if (el.attrs) {
            data += "attrs:{" + (genProps(el.attrs)) + "},";
        }

        // DOM props
        // g：原生属性
        if (el.props) {
            data += "domProps:{" + (genProps(el.props)) + "},";
        }

        // event handlers
        if (el.events) {
            data += (genHandlers(el.events, false, state.warn)) + ",";
        }

        // g：原生事件
        if (el.nativeEvents) {
            data += (genHandlers(el.nativeEvents, true, state.warn)) + ",";
        }

        // slot target
        // only for non-scoped slots
        if (el.slotTarget && !el.slotScope) {
            data += "slot:" + (el.slotTarget) + ",";
        }

        // scoped slots
        if (el.scopedSlots) {
            data += (genScopedSlots(el.scopedSlots, state)) + ",";
        }

        // component v-model
        // g：组件使用 v-model
        if (el.model) {
            data += "model:{value:" + (el.model.value) + ",callback:" + (el.model.callback) + ",expression:" + (el.model.expression) + "},";
        }

        // inline-template
        if (el.inlineTemplate) {
            var inlineTemplate = genInlineTemplate(el, state);
            if (inlineTemplate) {
                data += inlineTemplate + ",";
            }
        }

        data = data.replace(/,$/, '') + '}';

        // v-bind data wrap
        if (el.wrapData) {
            data = el.wrapData(data);
        }

        // console.log("el.wrapListeners",el.wrapListeners)

        // v-on data wrap
        // g：事件监听
        if (el.wrapListeners) {
            data = el.wrapListeners(data);
        }

        // log_event("genData$2 data",data)

        return data

    }

    function genDirectives(el, state) {

        var dirs = el.directives;

        if (!dirs) { return }

        var res = 'directives:[';
        var hasRuntime = false;
        var i, l, dir, needRuntime;

        for (i = 0, l = dirs.length; i < l; i++) {

            dir = dirs[i];
            needRuntime = true;

            // g：directives 保存 model，text，html
            var gen = state.directives[dir.name];

            if (gen) {
                // compile-time directive that manipulates AST.
                // returns true if it also needs a runtime counterpart.
                needRuntime = !!gen(el, dir, state.warn);
            }

            if (needRuntime) {
                hasRuntime = true;
                res += "{name:\"" + (dir.name) + 
                "\",rawName:\"" + (dir.rawName) + "\"" + 
                (dir.value ? (",value:(" + (dir.value) + "),expression:" + 
                (JSON.stringify(dir.value))) : '') + 
                (dir.arg ? (",arg:\"" + (dir.arg) + "\"") : '') + (dir.modifiers ? (",modifiers:" + (JSON.stringify(dir.modifiers))) : '') + "},";
            }

        }

        if (hasRuntime) {
            return res.slice(0, -1) + ']'
        }

    }

    function genInlineTemplate(el, state) {

        var ast = el.children[0];

        if ("development" !== 'production' && (
                el.children.length !== 1 || ast.type !== 1
            )) {
            state.warn('Inline-template components must have exactly one child element.');
        }

        if (ast.type === 1) {
            var inlineRenderFns = generate(ast, state.options);
            return ("inlineTemplate:{render:function(){" + (inlineRenderFns.render) + "},staticRenderFns:[" + (inlineRenderFns.staticRenderFns.map(function(code) { return ("function(){" + code + "}"); }).join(',')) + "]}")
        }
        
    }

    function genScopedSlots(
        slots, state
    ) {
        return ("scopedSlots:_u([" + (Object.keys(slots).map(function(key) {
            return genScopedSlot(key, slots[key], state)
        }).join(',')) + "])")
    }

    function genScopedSlot(
        key, el, state
    ) {

        if (el.for && !el.forProcessed) {
            return genForScopedSlot(key, el, state)
        }

        var fn = "function(" + (String(el.slotScope)) + "){" +
            "return " + (el.tag === 'template' ?
                el.if ?
                ((el.if) + "?" + (genChildren(el, state) || 'undefined') + ":undefined") :
                genChildren(el, state) || 'undefined' :
                genElement(el, state)) + "}";

        return ("{key:" + key + ",fn:" + fn + "}")
    }

    function genForScopedSlot(
        key, el,  state
    ) {

        var exp = el.for;
        var alias = el.alias;
        var iterator1 = el.iterator1 ? ("," + (el.iterator1)) : '';
        var iterator2 = el.iterator2 ? ("," + (el.iterator2)) : '';

        el.forProcessed = true; // avoid recursion

        return "_l((" + exp + ")," +
            "function(" + alias + iterator1 + iterator2 + "){" +
            "return " + (genScopedSlot(key, el, state)) +
            '})'
    }

    function genChildren(
        el, state, checkSkip,
        altGenElement,  altGenNode
    ) {

        var children = el.children;

        if (children.length) {

            var el$1 = children[0];

            // optimize single v-for
            if (children.length === 1 &&
                el$1.for &&
                el$1.tag !== 'template' &&
                el$1.tag !== 'slot'
            ) {
                return (altGenElement || genElement)(el$1, state)
            }

            var normalizationType = checkSkip ?
                getNormalizationType(children, state.maybeComponent) :
                0;

            var gen = altGenNode || genNode;

            return ("[" + (
                    children.map(function(c) { 
                        return gen(c, state); 
                    }
                    ).join(',')
                ) + "]" + (normalizationType ? ("," + normalizationType) : '')
            )

        }
    }

    // determine the normalization needed for the children array.
    // 0: no normalization needed
    // 1: simple normalization needed (possible 1-level deep nested array)
    // 2: full normalization needed
    function getNormalizationType(
        children,  maybeComponent
    ) {

        var res = 0;

        for (var i = 0; i < children.length; i++) {

            var el = children[i];

            if (el.type !== 1) {
                continue
            }

            if (needsNormalization(el) ||
                (el.ifConditions && el.ifConditions.some(function(c) { return needsNormalization(c.block); }))) {
                res = 2;
                break
            }

            if (maybeComponent(el) ||
                (el.ifConditions && el.ifConditions.some(function(c) { return maybeComponent(c.block); }))) {
                res = 1;
            }

        }
        return res
    }

    function needsNormalization(el) {
        return el.for !== undefined || el.tag === 'template' || el.tag === 'slot'
    }

    function genNode(node, state) {

        if (node.type === 1) {
            return genElement(node, state)
        }

        if (node.type === 3 && node.isComment) {
            return genComment(node)
        } 
        
        else {
            return genText(node)
        }

    }

    function genText(text) {

        return ("_v(" + (text.type === 2 ?
            text.expression // no need for () because already wrapped in _s()
            :
            transformSpecialNewlines(JSON.stringify(text.text))) + ")")

    }

    function genComment(comment) {
        return ("_e(" + (JSON.stringify(comment.text)) + ")")
    }

    function genSlot(el, state) {
        
        var slotName = el.slotName || '"default"';
        var children = genChildren(el, state);
        var res = "_t(" + slotName + (children ? ("," + children) : '');
        var attrs = el.attrs && ("{" + (el.attrs.map(function(a) { return ((camelize(a.name)) + ":" + (a.value)); }).join(',')) + "}");
        var bind$$1 = el.attrsMap['v-bind'];

        if ((attrs || bind$$1) && !children) {
            res += ",null";
        }

        if (attrs) {
            res += "," + attrs;
        }

        if (bind$$1) {
            res += (attrs ? '' : ',null') + "," + bind$$1;
        }

        return res + ')'
    }

    // componentName is el.component, take it as argument to shun flow's pessimistic refinement
    function genComponent(
        componentName, el, state
    ) {

        var children = el.inlineTemplate ? null : genChildren(el, state, true);

        return ("_c(" + componentName + "," + (genData$2(el, state)) + (children ? ("," + children) : '') + ")")
    }

    function genProps(props) {

        var res = '';

        for (var i = 0; i < props.length; i++) {
            var prop = props[i];
            /* istanbul ignore if */
            {
                res += "\"" + (prop.name) + "\":" + (transformSpecialNewlines(prop.value)) + ",";
            }
        }

        return res.slice(0, -1)
    }
    
    // g：el 就是 ast， 把 ast 逐步转成 render 字符串
    function genElement(el, state) {

        // g：下面的方法会递归调用
        // g：比如调用了 genFor 之后，for 执行了，genFor 重新调用 genElement 去执行其他东西

        if (el.staticRoot && !el.staticProcessed) {
            return genStatic(el, state)
        } 
        
        else if (el.once && !el.onceProcessed) {
            return genOnce(el, state)
        } 
        
        else if (el.for && !el.forProcessed) {
            return genFor(el, state)
        }
        
        else if (el.if && !el.ifProcessed) {
            return genIf(el, state)
        } 
        
        else if (el.tag === 'template' && !el.slotTarget) {
            return genChildren(el, state) || 'void 0'
        } 
        
        else if (el.tag === 'slot') {
            return genSlot(el, state)
        }
        
        else {

            // component or element
            var code;

            if (el.component) {
                code = genComponent(el.component, el, state);
            } 
            
            // g：终极创建节点，最后总是要回归到这里
            else {

                // g：当 el.pre=false 的时候，el.plain=true
                var data = el.plain ? undefined : genData$2(el, state);

                // g：遍历完父节点，马上遍历子节点，递归调用本方法，genData$2 一直下去
                var children = el.inlineTemplate ? null : genChildren(el, state, true);

                // g：就是 _c( tag, data, child )

                // g：这里的 data 是字符串
                // g：data:{ref:"child_b",attrs:{"aname":name},on:{"p-c-event":pcEvent}} 
                // g：_c('test-component',{ref:"child_b",attrs:{"aname":name},on:{"p-c-event":pcEvent}})
                // code = "_c('" + (el.tag) + "'" + 
                //         (data ?  ("," + data) : '') +
                //         (children ? ("," + children) : '') + ")";
                
                code = "_c('" + (el.tag) + "'" + 
                        (data ?  ("," + data) : '') +
                    // ',{on:{"p-c-event":pcEventa}}' +
                    (children ? ("," + children) : '') + ")";
                        // log_event("genElement code",code)

            }

            // module transforms
            // g：tansforms 的作用
            // parseLog("state.transforms.",state)
            for (var i = 0; i < state.transforms.length; i++) {
                code = state.transforms[i](el, code);
            }
            return code
        }
    }

    var CodegenState = function CodegenState(options) {

        this.options = options;
        this.warn = options.warn || baseWarn;
        this.transforms = pluckModuleFunction(options.modules, 'transformCode');
        this.dataGenFns = pluckModuleFunction(options.modules, 'genData');
        this.directives = extend(extend({}, baseDirectives), options.directives);

        var isReservedTag = options.isReservedTag || no;
        
        this.maybeComponent = function(el) { return !isReservedTag(el.tag); };
        this.onceId = 0;
        this.staticRenderFns = [];

    };

    
    // g：过滤得到 对象数组中 的具体某个属性
    // g：pluckModuleFunction([{a:1},{b:2},{c:3}],"c") 那么 数组中每个对象的 c
    function pluckModuleFunction(
        modules,  key
    ) {
        return modules ?

            // g：得到 [ undefined, undefined, 3]
            modules.map(function(m) { return m[key]; })

            // g：filter 的作用是过滤 undefined，因为有的对象可能没有 这个属性
            .filter(function(_) { return _; }) : []
    }
    
/**
 * Mix properties into target object.
 */
function extend(to, _from) {
    for (var key in _from) {
        to[key] = _from[key];
    }
    return to
}